编写一个简单的发布器(publisher)和订阅器(subscriber)

发布器

  1. mkdir -p ~/catkin_ws/src/beginner_tutorials/src先在beginner_tutorials下创建一个src。 -p的意思是如果路径中有不存在的文件夹,则创建
  2. 在新建的文件夹里面新建cpp文件,talker.cpp,将一下内容复制进去。
#include "ros/ros.h"

#include "std_msgs/String.h"

#include <sstream>


int main(int argc, char **argv)
{
    ros::init(argc, argv, "talker");


    ros::NodeHandle n;


    ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);

    ros::Rate loop_rate(10);

    int count = 0;
    while (ros::ok())
    {

        std_msgs::String msg;

        std::stringstream ss;
        ss << "hello world " << count;
        msg.data = ss.str();

        ROS_INFO("%s", msg.data.c_str());


        chatter_pub.publish(msg);

        ros::spinOnce();

        loop_rate.sleep();
        ++count;
}


return 0;
}
  1. #include “ros/ros.h” 是最常用的使用ros系统必须包含进去的头文件

  2. #include “std_msgs/String.h”另一个头文件,由String.msg自动生成。

  3. ros::init(argc, argv, “talker”);在使用ROS 系统之前都必须先初始化。参数有三个,前两个必须是argc argv,第三个是节点名字。ros::init()可以通过命令行进行名字的重映射。
  4. ros::NodeHandle n;创建一个talker的句柄,创建的第一个还会初始化节点,最后一个销毁的会清除node使用的资源(这里还没见过有创建多个的,不知道这里的销毁是不是多个节点的最后一个。
  5. ros::Publisher chatter_pub = n.advertise(“chatter”, 1000);告诉master我们要在chatter话题上发布std_msgs/String的消息,然后master会告诉所有正在收听cahtter的节点我们要发布消息了。第二个参数是我们发布队列的大小,如果我们发消息太快,超过了1000条信息,缓冲区会把之前的消息扔掉。 NodeHandle::advertise()返回一个ros::Publisher对象,有一个成员函数publish()可以让我们在topic上发布消息,如果类型不对,拒绝发布(不太懂)
  6. ros::Rate loop_rate(10); ros::Rate对象允许我们指定循环的频率。记录距离上一次Rate::sleep()调用的时间,然后休眠正确的时间,比如这里是10Hz一次循环。
  7.  int count = 0;
    while (ros::ok())
    {
    

    计算发布消息的次数,按ctrl+c将ros::ok()置为false 还有下面情况ros::ok()也会为false -被另一同名节点踢出ROS网络 -ros::shutdown()被程序的另一部分调用 -所有的ros::NodeHandles都已经被销毁

  8. chatter_pub.publish(msg);发布消息

  9. ROS_INFO(“%s”, msg.data.c_str());就是printf在ROS中的代替

  10. ros::spinOnce();使得回调函数(callback)能够被调用,加上最好
  11. loop_rate.sleep()通过sleep休眠来使得发布频率为10hz

订阅器

现在在beginner_tutorials package下创建src/listener.cpp。粘贴代码:

#include "ros/ros.h"
#include "std_msgs/String.h"


void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
  ROS_INFO("I heard: [%s]", msg->data.c_str());
}

int main(int argc, char **argv)
{

  ros::init(argc, argv, "listener");


  ros::NodeHandle n;

   ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);


  ros::spin();

  return 0;
}

只说与发布器不同的部分

  1. void chatterCallback(const std_msgs::String::ConstPtr& msg)
    {
    ROS_INFO("I heard: [%s]", msg->data.c_str());
    }
    

    回调函数,消息到达chatter topic时会被调用,消息是以boost shared_ptr指针的形式传输,我们可以存储它而不需要复制数据

  2. ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);这是告诉master我们要订阅chatter上的消息,有消息到达topic时,ROS会调用chatterCallback()的函数。第二个参数是队列的大小,同publicer的参数。返回ros::Subscriber。

  3. ros::spin();ros::spin()进入自循环,尽可能快的调用消息回调函数。如果没有消息到达,不会占用很多CPU,一旦ros::ok()返回FALSE,ros::spin()会立刻跳出自循环。

编译节点

将下列代码加到CMakeLilst.txt的尾

include_directories(include ${catkin_INCLUDE_DIRS})

add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)

add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)

这样可以加入两个可执行文件,talker和listener,这两个会自动在devel下生成,路径是~/catkin_ws/devel/lib/share/ add_dependencies(talker beginner_tutorials_generate_messages_cpp)添加对生成的消息文件的依赖 运行catkin_make

测试

  1. 运行roscore
  2. 运行talker

    rosrun beginner_tutorials talker

  3. 运行订阅器

    rosrun beginner_tutorials listener 可以看到两个终端分别是发布消息和订阅消息(hello world和heard hello world)